using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class Game : PersistableObject
{
    public KeyCode createKey = KeyCode.C;//创建
    public KeyCode newGameKey = KeyCode.N;//开始新游戏
    public KeyCode saveKey = KeyCode.S;//保存游戏
    public KeyCode loadKey = KeyCode.L;//加载数据
    public KeyCode destroyKey = KeyCode.X;//销毁物体

    List<Shape> shapes;//对象池

    public PersistentStorage storage;//持久化存储

    const int saveVersion = 6;//版本号

    public float CreationSpeed{get;set;}//读取新增滚动条进度
    float creationProgress;//跟踪创建进度
    public float DestructionSpeed{get;set;}//读取销毁滚动条进度
    float destructionProgress;//跟踪销毁进度

    public int levelCount;//关卡数量
    int loadedLevelBuildIndex;//跟踪当前已加载关卡的构建索引

    Random.State mainRandomState;//主随机种子
    [SerializeField]
    bool reseedOnLoad;//是否启用可重现的随机性

    //跟踪滑动条
    [SerializeField]
    Slider creationSpeedSlider;
    [SerializeField]
    Slider destructionSpeedSlider;

    [SerializeField]
    ShapeFactory[] shapeFactories;//工厂数组

    void Update()
    {
        if(Input.GetKeyDown(createKey))
        {//创建
            CreateShape();
        }else if(Input.GetKey(newGameKey))
        {//开始新游戏
            BeginNewGame();
            StartCoroutine(LoadLevel(loadedLevelBuildIndex));//开始新游戏时也加载关卡，从而重置整个关卡状态
        }else if(Input.GetKey(saveKey))
        {//保存
            storage.Save(this, saveVersion);
        }else if(Input.GetKey(loadKey))
        {//加载
            BeginNewGame();
            storage.Load(this);
        }else if(Input.GetKey(destroyKey))
        {//销毁
            DestroyShape();
        }else{//加载新场景
            for(int i = 1;i<= levelCount;i++)
            {
                if(Input.GetKeyDown(KeyCode.Alpha0+i))
                {
                    //卸载场景时会卸载所有已生成的对象，因此在加载另一个场景前先调用创建。
                    BeginNewGame();
                    StartCoroutine(LoadLevel(i));
                    return;
                }
            }
        }

        
        
    }

    //每次启用一个已经禁用的组件时都会调用它
    //如果在游戏模式下进行重新编译，则首先会禁用所有活动组件，然后保存游戏状态，进行编译，恢复游戏状态，并再次启用先前的活动组件。 
    //还有一个OnDisable方法，实际上它是在重新编译之前被调用的。
    //除非组件以禁用状态保存，否则OnEnable()会在组件的Awake方法后立即调用。
    void OnEnable()
    {//使用Onenable以便在热重载后重新生成ID。
        if(shapeFactories[0].FactoryId != 0)//游戏加载完成后也会调用OnEnable，这种情况下不应重新分配ID
        {
            for(int i = 0;i<shapeFactories.Length;i++)
            {
                shapeFactories[i].FactoryId = i;
            }
        }
        
    }

    void FixedUpdate()
    {//在Update之后，FixedUpdate会在每个帧中被调用。调用多少次取决于帧时间和固定时间步长，你可以通过“Edit/ Project Settings / Time”进行配置。
        
        for(int i = 0;i<shapes.Count;i++)
        {//遍历更新一次形状
            shapes[i].GameUpdate();
        }


        //通过添加从最后一帧开始的时间，在Update中增加进度，该时间通过Time.deltaTime获得。
        creationProgress += Time.deltaTime * CreationSpeed;
        while(creationProgress >= 1f)
        {//当进度足够时，创建新形状
            creationProgress -= 1f;
            CreateShape();
        }
        destructionProgress += Time.deltaTime * DestructionSpeed;
        while(destructionProgress >= 1f)
        {
            destructionProgress -= 1f;
            DestroyShape();
        }
    }

    void Start()
    {
        shapes = new List<Shape>();
        mainRandomState = Random.state;

        if(Application.isEditor)
        {//仅在编辑器中才检查
            for(int i = 0;i<SceneManager.sceneCount;i++)
            {//检查是否已经加载场景，防止多次加载
                Scene loadedScene = SceneManager.GetSceneAt(i);
                if(loadedScene.name.Contains("Level "))
                {
                    SceneManager.SetActiveScene(loadedScene);
                    loadedLevelBuildIndex = loadedScene.buildIndex;
                    return;
                }
            }
        }
        BeginNewGame();
        StartCoroutine(LoadLevel(1));//协程加载场景
    }

    

    public override void Save(GameDataWriter writer)
    {
        writer.Write(shapes.Count);
        writer.Write(Random.state);
        writer.Write(CreationSpeed);
        writer.Write(creationProgress);
        writer.Write(DestructionSpeed);
        writer.Write(destructionProgress);
        writer.Write(loadedLevelBuildIndex);//保存当前关卡索引
        GameLevel.Current.Save(writer);//写入关卡数据

        for(int i = 0;i<shapes.Count;i++)
        {
            writer.Write(shapes[i].OriginFactory.FactoryId);
            writer.Write(shapes[i].ShapeId);
            writer.Write(shapes[i].MaterialId);
            shapes[i].Save(writer);
        }
    }

    public override void Load(GameDataReader reader)
    {
        int version = reader.Version;
        if(version>saveVersion)
        {//加载版本比当前保存版本高
            Debug.LogError("Unsupported future save version " + version);
            return;
        }
        StartCoroutine(LoadGame(reader));
        
    }

    IEnumerator LoadGame(GameDataReader reader)
    {//只有在加载关卡之后才能读取关卡数据，所以我们将加载过程变为协程
        int version = reader.Version;
        int count = version <= 0? -version : reader.ReadInt();
        if(version >= 3){
            Random.State state = reader.ReadRandomState();
            if(!reseedOnLoad){//加载随机种子
                Random.state = state;
            }
            creationSpeedSlider.value = CreationSpeed = reader.ReaderFloat();
            destructionSpeedSlider.value = creationProgress = reader.ReaderFloat();
            DestructionSpeed = reader.ReaderFloat();
            destructionProgress = reader.ReaderFloat();
        }
        yield return LoadLevel(version < 2 ? 1 : reader.ReadInt());//先完成加载场景
        if(version >= 3){
            GameLevel.Current.Load(reader);
        }
        for(int i = 0; i<count;i++)
        {
            int factoryId = version >= 5 ? reader.ReadInt() : 0;
            int shapeId = version > 0 ?  reader.ReadInt() : 0;
            int materialId = version > 0 ? reader.ReadInt() : 0;
            Shape instance = shapeFactories[factoryId].Get(shapeId, materialId);
            instance.Load(reader);
            shapes.Add(instance);
        }
    }


    void BeginNewGame()
    {
        
        Random.state = mainRandomState;//恢复主随机状态
        int seed = Random.Range(0, int.MaxValue);//获取一个随机值
        mainRandomState = Random.state;
        Random.InitState(seed);//将随机值通过random初始化一个新的伪随机序列

        creationSpeedSlider.value = CreationSpeed = 0;
        destructionSpeedSlider.value = DestructionSpeed = 0;

        for(int i = 0;i<shapes.Count;i++)
        {
            shapes[i].Recycle();//删除对象
        }
        shapes.Clear();
    }

    void CreateShape()
    {
        shapes.Add(GameLevel.Current.SpawnShape());
    }

    //销毁形状
    void DestroyShape()
    {
        if(shapes.Count > 0)
        {
            int index = Random.Range(0, shapes.Count);
            shapes[index].Recycle();

            //把要删除的对象放到列表末尾，然后删除列表末尾来保证列表中没有空元素
            int lastIndex = shapes.Count - 1;
            shapes[index] = shapes[lastIndex];
            shapes.RemoveAt(lastIndex);//从列表中删除
        }
        
    }

    IEnumerator LoadLevel(int levelBuildIndex)
    {//加载场景
        enabled = false;//加载时禁用自身

        if(loadedLevelBuildIndex > 0)
        {
            yield return SceneManager.UnloadSceneAsync(loadedLevelBuildIndex);//异步卸载老场景
        }

        //LoadSceneAsync异步加载场景
        //LoadSceneMode.Additive 在已加载的场景之外加载场景
        //产生一次延迟返回，使场景加载完毕再调用下面的激活场景。
        yield return SceneManager.LoadSceneAsync(levelBuildIndex, LoadSceneMode.Additive);//根据索引加载场景
        SceneManager.SetActiveScene(SceneManager.GetSceneByBuildIndex(levelBuildIndex));
        loadedLevelBuildIndex = levelBuildIndex;
        enabled = true;//加载后恢复
    }

}
